__all__ = ['Monster', 'Map']

from tower import *
POS_DIMESION = 2

class Monster(object):
    def __init__(self, max_hp, speed, defence, position, goal):
        self.MAX_HP = max_hp
        self.hp = max_hp
        self._speed = speed
        self.defence = defence
        self.position = position
        self._distance_moved = 0

        # Note: goal = {'pos':p, 'id':i}
        self.goal = goal
        self.direction = Monster.direction_of_2pos(position, goal['pos'])      

        self._frozen_time = 0
        self._frozen_rate = 0

    def attacked_by(self, firepower):
        self.hp -= Monster._damage(firepower, self.defence)
        if self.hp < 0:
            self.hp = 0

    def fire_attacked_by(self, firepower):
        self.hp -= firepower
        if self.hp < 0:
            self.hp = 0

    def freeze(self, time, rate):
        self._frozen_rate = max(self._frozen_rate, rate)
        self._frozen_time = max(self._frozen_time, time)

    def frozen(self):
        return self._frozen_rate != 0

    def dead(self):
        return self.hp <= 0

    def update_state(self):
        self._frozen_time -= 1
        if self._frozen_time == 0:
            self._frozen_rate = 0

    def speed(self):
        return self._speed * (1 - self._frozen_rate)

    def walk(self):
        s = self.speed()
        d = self.direction
        pos = self.position
        self.position = (pos[0] + s * d[0] , pos[1] + s * d[1])
        self._distance_moved += self.speed()

    def change_goal(self, g):
        #debug
        #print "change_goal", g

        self.direction = Monster.direction_of_2pos(self.goal['pos'], g['pos'])
        self.goal = g

    def dist_moved(self):
        return self._distance_moved

    @classmethod
    def _damage(cls ,attack, defence):
        _DEF_CONSTANT = 100
        defence_rate = defence / (_DEF_CONSTANT + defence)
        return attack * (1 - defence_rate)

    @classmethod
    def direction_of_2pos(cls, a, b):
        def f(x):
            if x == 0: return 0
            return 1 if x > 0 else -1
        return (f(b[0] - a[0]), f(b[1] - a[1]))


class Map(object):
    def __init__(self, width, height, path):
       self.width = width
       self.height = height
       self._path = path

       self._monsters = []
       self._towers = []

    def monsters(self):
        return self._monsters

    def targets(self, position, atk_range):
        targets = []
        p_x, p_y = position

        def in_atk_range(monster):
            x, y = monster.position
            dx, dy = abs(x - p_x), abs(y - p_y)
            return dx**2 + dy ** 2 <= atk_range ** 2
        
        for monster in self._monsters :
            if in_atk_range(monster):
                targets.append(monster)
        return targets

    def add_monster(self, monster):
        self._monsters.append(monster)

    def move_monster(self, monster):
        def go_over(m):
            def f(i):
                return (m.goal['pos'][i] - m.position[i]) * m.direction[i] <= 0
            if f(0) and f(1):
                return True
            return False

        monster.walk()

        if monster.dead():
            d = monster.dist_moved()
            del self._monsters[ self._monsters.index(monster) ]
            return d

        if go_over(monster):
            monster.position = monster.goal['pos']
            n = monster.goal['id'] + 1
            if n >= len(self._path):
                d = monster.dist_moved()
                del self._monsters[ self._monsters.index(monster) ]
                return d
            monster.change_goal({'pos':self._path[n], 'id': n})

        return True

    def road(self): return self._path
